V: Encodable<json::Encoder<'a>, io::IoError>>(
exec: fn(T, U, &mut MultiShell) -> CliResult<Option<V>>,
options_first: bool) {
- process::<V>(|rest, shell| call_main(exec, shell, rest, options_first));
+ // see comments below
+ off_the_main_thread(proc() {
+ process::<V>(|rest, shell| call_main(exec, shell, rest, options_first));
+ });
}
pub fn call_main<'a,
V: Encodable<json::Encoder<'a>, io::IoError>>(
exec: fn(T, &mut MultiShell) -> CliResult<Option<V>>,
options_first: bool) {
- process::<V>(|rest, shell| call_main_without_stdin(exec, shell, rest,
- options_first));
+ // see comments below
+ off_the_main_thread(proc() {
+ process::<V>(|rest, shell| call_main_without_stdin(exec, shell, rest,
+ options_first));
+ });
}
pub fn call_main_without_stdin<'a,
CliError::new("Could not process standard in as input", 1)
})
}
+
+// Seems curious to run cargo off the main thread, right? Well do I have a story
+// for you. Turns out rustdoc does a similar thing, and already has a good
+// explanation [1] though, so I'll just point you over there.
+//
+// [1]: https://github.com/rust-lang/rust/blob/85fd37f/src/librustdoc/lib.rs#L92-L122
+fn off_the_main_thread(p: proc():Send) {
+ let (tx, rx) = channel();
+ spawn(proc() { p(); tx.send(()); });
+ if rx.recv_opt().is_err() {
+ std::os::set_exit_status(std::rt::DEFAULT_ERROR_CODE);
+ }
+}
pub fn checkout(&self, into: &Path) -> CargoResult<GitDatabase> {
let repo = if into.exists() {
let r = try!(git2::Repository::open(into));
- try!(self.fetch_into(&r));
+ try!(self.fetch_into(&r).chain_error(|| {
+ internal(format!("failed to fetch into {}", into.display()))
+ }));
r
} else {
- try!(self.clone_into(into))
+ try!(self.clone_into(into).chain_error(|| {
+ internal(format!("failed to clone into: {}", into.display()))
+ }))
};
Ok(GitDatabase { remote: self.clone(), path: into.clone(), repo: repo })
fn clone_into(&self, dst: &Path) -> CargoResult<git2::Repository> {
let url = self.url.to_string();
+ try!(mkdir_recursive(dst, UserDir));
let repo = try!(git2::build::RepoBuilder::new().bare(true)
.hardlinks(false)
.clone(url.as_slice(), dst));
// If the git checkout already exists, we don't need to clone it again
let repo = match git2::Repository::open(into) {
Ok(repo) => repo,
- Err(..) => try!(GitCheckout::clone_repo(database.get_path(), into)),
+ Err(..) => {
+ try!(mkdir_recursive(&into.dir_path(), UserDir));
+ try!(GitCheckout::clone_repo(database.get_path(), into))
+ }
};
Ok(GitCheckout {
location: into.clone(),
let url = try!(source.to_url().map_err(human));
let url = url.to_string();
- let repo = try!(git2::Repository::clone(url.as_slice(), into));
+ let repo = try!(git2::Repository::clone(url.as_slice(),
+ into).chain_error(|| {
+ internal(format!("failed to clone {} into {}", source.display(),
+ into.display()))
+ }));
Ok(repo)
}
.with_stdout(format!("{} git repository `[..]`\n\
{} dep1 v0.5.0 ([..])\n\
{} foo v0.5.0 ([..])\n\
- {} `target/foo`\n\
+ {} `target[..]foo`\n\
project2\
",
UPDATING,
let mut file = File::create(&git_project.root().join(".gitmodules"));
file.write_str(format!("[submodule \"src\"]\n\tpath = src\n\turl={}",
- git_project3.root().display()).as_slice());
+ git_project3.url()).as_slice());
git_project.process("git").args(["submodule", "sync"]).exec_with_output().assert();
git_project.process("git").args(["fetch"]).cwd(git_project.root().join("src"))
.with_stdout(format!("{} git repository `[..]`\n\
{} dep1 v0.5.0 ([..])\n\
{} foo v0.5.0 ([..])\n\
- {} `target/foo`\n\
+ {} `target[..]foo`\n\
project3\
",
UPDATING,